package com.hqd.ch03.v27.aop.framework.autoproxy;

import com.hqd.ch03.v27.aop.Advisor;
import com.hqd.ch03.v27.aop.TargetSource;
import com.hqd.ch03.v27.core.OrderComparator;
import com.hqd.ch03.v27.factory.BeanFactory;
import com.hqd.ch03.v27.factory.ListableBeanFactory;
import com.hqd.ch03.v27.utils.AopUtils;
import com.hqd.ch03.v27.utils.AspectJProxyUtils;

import java.util.List;

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
    private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        initBeanFactory((ListableBeanFactory) beanFactory);
    }

    protected void initBeanFactory(ListableBeanFactory beanFactory) {
        this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelper(beanFactory);
    }

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(
            Class<?> beanClass, String beanName, TargetSource targetSource) {

        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

    private List<Advisor> sortAdvisors(List<Advisor> eligibleAdvisors) {
        if (eligibleAdvisors.size() > 1) {
            eligibleAdvisors.sort(OrderComparator.INSTANCE);
        }
        return eligibleAdvisors;
    }

    /**
     * 拓展拦截器
     *
     * @param candidateAdvisors
     */
    protected void extendAdvisors(List<Advisor> candidateAdvisors) {
        AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
    }

    protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

        ProxyCreationContext.setCurrentProxiedBeanName(beanName);
        try {
            return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        } finally {
            ProxyCreationContext.setCurrentProxiedBeanName(null);
        }
    }

    protected List<Advisor> findCandidateAdvisors() {
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }
}
